
import { ERR_SWITCH_STATE } from "../../../extention/return_code";
import { GotoOpts, jobGoto } from "../../../job/goto";
import { jobTransfer } from "../../../job/transfer";
import { jobWithdraw } from "../../../job/withdraw";
import { getApproprateEnergy, getCreepCountCoeficient, getMinEnergy, spawnCreep } from "../../../role/spawn";
import { UPGRADER_DEMENDING_ENERGY_PERCENTAGE, UPGRADER_NORMAL_ENERGY_PERCENTAGE, UPGRADER_SUPPLYING_ENERGY_PERCENTAGE } from "../../../util/constants";
import { getActualResourceAmount, getCapacity, transportResource } from "../../../util/store";
import { GroupWork } from "./work";

export class GroupUpgraders extends GroupWork{
    constructor(spawnRoom){
        super(spawnRoom);
    }
    get maxSize(){
        if(this.spawnRoom.controller.level >= 8){
            return 1;
        }else{
            return 4;
        }
    }
    serialize(){
        return {
            super:super.serialize(),
            containerId:this._containerId,
        };
    }
    deserialize(obj){
        super.deserialize(obj.super);
        this._containerId = obj.containerId;

        if(this.container && getActualResourceAmount(this.container) <= 100 && getCreepCountCoeficient(this.spawnRoom) >= 0.8){
            jobTransfer.getTargets(this.spawnRoom).push(this.container);
        }

        return this;
    }
    /**
     * 
     * @param {Creep[]} creeps 
     */
    share(creeps){
        let storeI,storeJ,coefI,coefJ,capacitySum,storeSum,more,less;
        for(let i=0; i<creeps.length; i++){
            for(let j=i+1; j<creeps.length; j++){
                if(!(creeps[i].pos.isNearTo(creeps[j].pos))){
                    continue;
                }
                storeI = creeps[i].store;
                storeJ = creeps[j].store;

                coefI = storeI[RESOURCE_ENERGY]/storeI.getCapacity();
                coefJ = storeJ[RESOURCE_ENERGY]/storeJ.getCapacity();
                
                more,less;
                if(coefI === coefJ){
                    continue;
                }
                if(coefI > coefJ){
                    more = creeps[i];
                    less = creeps[j];
                }else{
                    more = creeps[j];
                    less = creeps[i];
                }
                capacitySum = storeI.getCapacity() + storeJ.getCapacity();
                storeSum = storeI[RESOURCE_ENERGY] + storeJ[RESOURCE_ENERGY];
                more.transfer(less, RESOURCE_ENERGY, more.store[RESOURCE_ENERGY]/more.store.getCapacity() - storeSum/capacitySum)
            }
        }
    }
    spawn(){
        let newCreep = undefined;
        const minimumTemplate = {
            require: [WORK,MOVE,CARRY],
            carry: .10,
        }
        const normalTemplate = {
            require: [WORK,MOVE,CARRY],
            move: .20,
            carry: .60,
            work: .20,
        }
        const initOpts={
            memory: {
                role: 'upgrader',
                working: true,
            }
        }

        let energy;
        let template;
        if(this.spawnRoom.controller.level === 8){
            energy = Math.max(200, getMinEnergy(this.spawnRoom));
            template = minimumTemplate;
        }else{
            energy = getApproprateEnergy(this.spawnRoom);

            energy = (energy-300)*(2/3)+300;
            template = normalTemplate;
        }

        newCreep = spawnCreep(this.spawnRoom, template, initOpts, energy)
        if(newCreep instanceof Creep){
            this.addCreep(newCreep);
            return OK;
        }
        return newCreep;
    }
    /**
     * @returns {StructureContainer|null}
     */
    get container(){
        if(this._container !== undefined){
            return this._container;
        }
        if(!this._containerId){
            return this._container = null;
        }
        return this._container = Game.getObjectById(this._containerId);
    }
    /**
     * amounts:
     * normal - 0.6
     * lack - 0.05
     * overflow - 0.85
     * 
     * @param {AnyCreep} creep 
     * @param {MoveToOpts|GotoOpts|undefined} opts 
     */
    shareResource(creep, opts = undefined){
        if(!this.container){
            return OK;
        }

        let ret = OK;

        const actualResource = getActualResourceAmount(creep);
        const capacity = getCapacity(creep);

        const isLowOnEnergy = actualResource < UPGRADER_DEMENDING_ENERGY_PERCENTAGE * capacity;
        if(isLowOnEnergy){
            const adjustingAmount = capacity * UPGRADER_NORMAL_ENERGY_PERCENTAGE - actualResource;
            ret = transportResource(this.container, creep, RESOURCE_ENERGY, adjustingAmount);
            if(ret === ERR_NOT_IN_RANGE){
                jobGoto.run(creep, [this.container], opts);
            }
            return ret;
        }

        const isHighOnEnergy = actualResource > UPGRADER_SUPPLYING_ENERGY_PERCENTAGE * capacity;
        if(isHighOnEnergy){
            const adjustingAmount = actualResource - capacity * UPGRADER_NORMAL_ENERGY_PERCENTAGE;
            ret = transportResource(creep, this.container, RESOURCE_ENERGY, adjustingAmount);
            if(ret === ERR_NOT_IN_RANGE){
                jobGoto.run(creep, [this.container], opts);
            }
            return ret;
        }

        return OK;
    }
    /**
     * 
     * @param {AnyCreep} creep 
     */
    withdraw(creep){
        // let targets = this.getContainers();
        // let ret = ERR_NOT_FOUND;
        // for(let target of targets){
        //     ret = creep.withdraw(target, RESOURCE_ENERGY);
        //     if(ret === OK){
        //         break;
        //     }
        // }
        // return ret;
        if(this.container.pos.isEqualTo(creep.pos)){
            return transportResource(this.container, creep, RESOURCE_ENERGY);
        }
        return ERR_NOT_IN_RANGE;
    }
    run(...args){
        if(!this.container){
            this.__withdrawTargets = jobWithdraw.getTargets(this.spawnRoom);
        }else{
            this.__withdrawTargets = [this.container];
        }
        const ret = super.run(...args);
        this.__withdrawTargets = undefined;
        return ret;
    }
    creepGet(creep){
        if(this.withdraw(creep) === OK){
            return OK;
        }
        let ret = jobWithdraw.run(creep, this.__withdrawTargets);

        if(creep.store[RESOURCE_ENERGY]/creep.store.getFreeCapacity()>0.5){
            return ERR_SWITCH_STATE;
        }
        return ret;
    }

    /**
     * 
     * @param {AnyCreep} creep 
     * @param {Boolean} forceStay 
     * @returns {CreepActionReturnCode}
     */
    creepWork(creep, forceStay = false){
        const controller = this.spawnRoom.controller;
        if(!controller){
            return ERR_NOT_FOUND;
        }

        const ret = creep.upgradeController(controller);
        const defaultMoveOpts = {visualizePathStyle:{stroke:"#f0f0f0"}}
        if(!controller.pos.inRangeTo(creep, 3)){
            jobGoto.run(creep, [controller], defaultMoveOpts);
        }else{
            this.shareResource(creep);
        }
        if(!forceStay && getActualResourceAmount(creep) === 0){
            return ERR_SWITCH_STATE;
        }
        return ret;
    }
}